#!/usr/bin/env python
# coding: utf-8

# In[1]:


import geopandas as gpd
from shapely.geometry import Point
import shapely
from h3 import h3
import json
import asyncio
from aiohttp import web
import aiohttp_cors
import nest_asyncio
nest_asyncio.apply()


# # 初始化

# In[11]:


LEVEL = 8


# In[2]:


level3 = gpd.read_file('gadm36_CHN_3.shp', encoding="utf-8")


# In[3]:


level3 = level3[["NL_NAME_1","NL_NAME_2","NL_NAME_3","geometry"]]


# In[4]:


level3 = level3.rename(columns = {
    "NL_NAME_1": "province",
    "NL_NAME_2": "city",
    "NL_NAME_3": "area"
})


# In[5]:


level3["province"] = level3["province"].apply(lambda x: x.split("|")[-1])
level3["city"] = level3["city"].apply(lambda x: x.split("|")[0])
level3["area"] = level3["area"].apply(lambda x: x.split("|")[0] if x else x)


# In[6]:


level3["length"] = level3["geometry"].apply(
    lambda x: 1 if isinstance(x, shapely.geometry.polygon.Polygon) else len(x))


# In[7]:


level3.loc[level3["area"].apply(lambda x: x==None), "area"] =     level3.loc[level3["area"].apply(lambda x: x==None), "city"]


# In[8]:


#记录level3中index与省市区对应的关系
index_geo_dict = {}


# In[9]:


for index, row in level3.iterrows():
    province = row["province"]
    city = row["city"]
    area = row["area"]
    index_geo_dict[index] = [province, city, area]


# In[10]:


def coord_to_list(coords):
    reverse_coords = coords[0]
    reverse_coords = [[x[1], x[0]] for x in reverse_coords]
    return [reverse_coords]


# In[15]:


import sys


# In[12]:


#记录每个h3_index与level3 index的对应关系
geo_dict = {}
for index, row in level3.iterrows():
    length = row["length"]
    if length == 1:
        geo_json = {
        "type":"Polygon",
        "coordinates":coord_to_list([list(row["geometry"].exterior.coords)])
        }
        h3_index = list(h3.polyfill(geo_json, LEVEL))
        for key in h3_index:
                geo_dict[key] = index
    else:
        for i in range(length):
            geo_json = {
            "type":"Polygon",
            "coordinates":coord_to_list([list(row["geometry"][i].exterior.coords)])
            }
            h3_index = list(h3.polyfill(geo_json, LEVEL))
            for key in h3_index:
                geo_dict[key] = index
    if index % 50 == 0:
        print(index)


# In[13]:


with open(f"geo_dict_{LEVEL}.json", "w") as f:
    f.write(json.dumps(geo_dict))


# In[14]:


with open("index_geo_dict.json", "w") as f:
    f.write(json.dumps(index_geo_dict))


# In[22]:


def reverse_coder(lng, lat, level=3):
    """地理逆编码
    """
    if isinstance(lng, float):
        lng = float(lng)
    if isinstance(lat, float):
        lat = float(lat)
    h3_index = h3.geo_to_h3(lat, lng, LEVEL)
    
    index_geo = geo_dict[h3_index]
    province, city, area = index_geo_dict[index_geo]
    
    if level == 1:
        return [province]
    elif level == 2:
        return [province, city]
    else:
        return [province, city, area]


# In[25]:


get_ipython().run_cell_magic('timeit', '', 'reverse_coder(120,30)')


# In[18]:


@asyncio.coroutine
async def interface_reverse_coder(request):
    
    try:
        info_json = await request.json() 
        level = 3
        area_name_list = []

        if "level" in info_json:
            level = info_json["level"]
        loc = info_json["loc"]
        
        for point in loc:      
            area_name_list.append(reverse_coder(point[0], point[1], level))
        
        return web.Response(text = json.dumps({
            "code": 200,
            "data": area_name_list
        }, ensure_ascii=False))
    except Exception as e:
        traceback.print_exc()
        return web.json_response({
            "code": 0,
            "data": repr(e)
        })


# In[ ]:


app = web.Application()

app.router.add_route("POST", "/reverse/geocoder", interface_reverse_coder)
app.router.add_route("GET", "/reverse/geocoder", interface_reverse_coder)

# Configure default CORS settings.
cors = aiohttp_cors.setup(app, defaults={
    "*": aiohttp_cors.ResourceOptions(
            allow_credentials=True,
            expose_headers="*",
            allow_headers="*",
        )
})

# Configure CORS on all routes.
for route in list(app.router.routes()):
    cors.add(route)

web.run_app(app,port=2333)

